package game.checkers;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import util.Pair;

import exceptions.MatchAlreadyStartedException;
import exceptions.MatchNotFinishedException;
import exceptions.InvalidMoveException;
import exceptions.MaximumPlayerNumberReachedException;
import exceptions.NotEnoughPlayersException;
import game.Match;
import game.Player;
import game.PlayerMove;


/**
 * Connect4Match
 * 
 * Represents a Connect4 match.
 */
public class CheckersMatch implements Match {
	
	/**
	 * Constructor.
	 */
	public CheckersMatch() {	
		this.id = hashCode();
		this.board = null;	
		this.winner = null;
		this.isFinished = false;
	}
	
	
	public int getId() {
		return this.id;
	}
	
	
	public boolean play(Player player, PlayerMove move) throws InvalidMoveException {
		
		try {
			
			CheckersBoard.State state = this.board.play(player,((CheckersMove)move));
							
			this.isFinished = state == CheckersBoard.State.Finished || state == CheckersBoard.State.Tied;
		
			if (this.isFinished) {
				
				if (state == CheckersBoard.State.Finished) {
					this.winner = player;
				}
				this.dateEnded = new Date();
			}
			
			this.nextPlayer = (this.playerA.equals(player)) ? this.playerB : this.playerA;
			return this.isFinished;
		} catch (RuntimeException ex) {			
			throw new InvalidMoveException(player, this, move);
		}
	}
	
	
	public void giveUp(Player player) {
		if (!this.isFinished) {
			this.isFinished = true;
			this.winner = (this.playerA.equals(player)) ? this.playerB : this.playerA;
			
			this.dateEnded = new Date();
		}
	}


	public boolean isValidMove(Player player, PlayerMove move) {
		
		return this.board.isValidMove(player, move);
	}
	

	public void start() {

		if (this.dateStarted != null)
			throw new MatchAlreadyStartedException(this);
		
		if (this.playerA == null || this.playerB == null)
			throw new NotEnoughPlayersException(this);
		
		this.nextPlayer = (Math.random() > 0.5 ? this.playerA : this.playerB);
		//inicia a nova Board
		this.board = new CheckersBoard(this.playerA,this.playerB);	
		this.dateStarted = new Date();
	}
	
	
	public boolean isFinished() {
		return this.isFinished;
	}
	
	public void addPlayer(Player player) {
		if (this.playerA == null)
			this.playerA = player;
		else if (this.playerB == null)
			this.playerB = player;
		else
			throw new MaximumPlayerNumberReachedException(this);
	}
	
	
	public List<Player> getPlayers() {
		List<Player> list = new ArrayList<Player>();
		list.add(this.playerA);
		list.add(this.playerB);
		
		return Collections.unmodifiableList(list);
	}
	
	
	public Player getWinner() throws MatchNotFinishedException {
		if (!this.isFinished)
			throw new MatchNotFinishedException(this);
		
		return this.winner;
	}
	
	
	public List<Pair<Player, Integer>> getRankingPoints()
	throws MatchNotFinishedException {
		if (!this.isFinished)
			throw new MatchNotFinishedException(this);
		
		ArrayList<Pair<Player, Integer>> list = new ArrayList<Pair<Player, Integer>>();
		
		int playerApoints = 0, 
			playerBpoints = 0;
		
		if (this.playerA.getRanking() > this.playerB.getRanking()) {
			if (this.playerA == this.winner) {
				playerApoints = 2;
				playerBpoints = -1;
			}
			else if (this.winner != null) {
				playerApoints = -2;
				playerBpoints = 4;
			}
			else {
				playerApoints = 0;
				playerBpoints = 2;
			}
		} else {
			if (this.playerA == this.winner) {
				playerApoints = 4;
				playerBpoints = -2;
			}
			else if (this.winner != null) {
				playerApoints = -1;
				playerBpoints = 2;
			}
			else {
				playerApoints = 2;
				playerBpoints = 0;
			}
		}
		
		
		list.add(new Pair<Player, Integer>(this.playerA, playerApoints));
		list.add(new Pair<Player, Integer>(this.playerB, playerBpoints));
		
		return list;
	}
	
	
	public List<Pair<Player, Integer>> getScore() throws MatchNotFinishedException {
		if (!this.isFinished)
			throw new MatchNotFinishedException(this);
		
		ArrayList<Pair<Player, Integer>> list = new ArrayList<Pair<Player, Integer>>();
		int playerAscore, playerBscore;
		
		if (winner != null) {
			// The winner as 1 point while the loser has 0.
			playerAscore = (winner.equals(playerA) ? 1 : 0);
			playerBscore = (winner.equals(playerB) ? 1 : 0);
		} else {	
			// Tie
			playerAscore = 1;
			playerBscore = 1;		
		}
		
		list.add(new Pair<Player, Integer>(this.playerA, playerAscore));
		list.add(new Pair<Player, Integer>(this.playerB, playerBscore));
		
		return list;
	}
	
	
	public int countMoves() {
		return 0;//this.board.countMoves();
	}


	public Player getNextPlayer() {
		return this.nextPlayer;
	}

	
	public Date getDateStarted() {
		return this.dateStarted;
	}
	
	
	public Date getDateEnded() throws MatchNotFinishedException {
		if (!this.isFinished)
			throw new MatchNotFinishedException(this);
		
		return this.dateEnded;
	}
			

	private CheckersBoard board;
	private Player winner;
	private Player playerA, playerB;
	private Player nextPlayer;
	private boolean isFinished = false;
	private Date dateStarted, dateEnded;
	private int id;
	
	private static final long serialVersionUID = 1L;
}
